<!DOCTYPE html>
<html lang="en">
  <head>
    <title>
        Java-framework-SpringMVC-2 - rulerLwx Blog
      </title>
        <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
    <meta name="renderer" content="webkit">
    <meta http-equiv="Cache-Control" content="no-transform" />
    <meta http-equiv="Cache-Control" content="no-siteapp" />
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="format-detection" content="telephone=no,email=no,adress=no">
    
    <meta name="theme-color" content="#000000" />
    
    <meta http-equiv="window-target" content="_top" />
    
    
    <meta name="description" content="数据格式转换 入参 数据格式化，如把日期格式化为 yyyy-MM-dd。 mvc:annotation-driven/配置的作用 mvc:annota" />
    <meta name="generator" content="Hugo 0.73.0 with theme pure" />
    <title>Java-framework-SpringMVC-2 - rulerLwx Blog</title>
    
    
    <link rel="stylesheet" href="https://rulerLwx.gitee.io/css/style.min.c4bc7071f132c964c2116bca53b392933f377e5ca7b7051ed245187c621a2d3e.css">
    
    <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css" async>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.css" async>
    <meta property="og:title" content="Java-framework-SpringMVC-2" />
<meta property="og:description" content="数据格式转换 入参 数据格式化，如把日期格式化为 yyyy-MM-dd。 mvc:annotation-driven/配置的作用 mvc:annota" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://rulerLwx.gitee.io/2020/07/java-framework-springmvc-2/" />
<meta property="article:published_time" content="2020-07-01T18:58:47+08:00" />
<meta property="article:modified_time" content="2020-07-01T18:58:47+08:00" />
<meta itemprop="name" content="Java-framework-SpringMVC-2">
<meta itemprop="description" content="数据格式转换 入参 数据格式化，如把日期格式化为 yyyy-MM-dd。 mvc:annotation-driven/配置的作用 mvc:annota">
<meta itemprop="datePublished" content="2020-07-01T18:58:47&#43;08:00" />
<meta itemprop="dateModified" content="2020-07-01T18:58:47&#43;08:00" />
<meta itemprop="wordCount" content="8479">



<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="Java-framework-SpringMVC-2"/>
<meta name="twitter:description" content="数据格式转换 入参 数据格式化，如把日期格式化为 yyyy-MM-dd。 mvc:annotation-driven/配置的作用 mvc:annota"/>

    <!--[if lte IE 9]>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
      <![endif]-->

    <!--[if lt IE 9]>
        <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
      <![endif]-->
  </head>

  
  

  <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
    <div class="slimContent">
      <div class="navbar-header">
        <div class="profile-block text-center">
          <a id="avatar" href="https://gitee.com/rulerLwx" target="_blank">
            <img class="img-circle img-rotate" src="https://rulerLwx.gitee.io/avatar.png" width="200" height="200">
          </a>
          <h2 id="name" class="hidden-xs hidden-sm">rulerLwx</h2>
          <h3 id="title" class="hidden-xs hidden-sm hidden-md">thinking...</h3>
          <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Guangzhou, China</small>
        </div><div class="search" id="search-form-wrap">
    <form class="search-form sidebar-form">
        <div class="input-group">
            <input type="text" class="search-form-input form-control" placeholder="Search" />
            <span class="input-group-btn">
                <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
                        class="icon icon-search"></i></button>
            </span>
        </div>
        <div class="ins-search">
            <div class="ins-search-mask"></div>
            <div class="ins-search-container">
                <div class="ins-input-wrapper">
                    <input type="text" class="ins-search-input" placeholder="Type something..."
                        x-webkit-speech />
                    <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
                        aria-label="Close"><span aria-hidden="true">×</span></button>
                </div>
                <div class="ins-section-wrapper">
                    <div class="ins-section-container"></div>
                </div>
            </div>
        </div>
    </form>
</div>
        <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
      </div>
      <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
        <ul class="nav navbar-nav main-nav">
            <li class="menu-item menu-item-home">
                <a href="/">
                    <i class="icon icon-home-fill"></i>
                  <span class="menu-title">Home</span>
                </a>
            </li>
            <li class="menu-item menu-item-archives">
                <a href="/posts/">
                    <i class="icon icon-archives-fill"></i>
                  <span class="menu-title">Archives</span>
                </a>
            </li>
            <li class="menu-item menu-item-categories">
                <a href="/categories/">
                    <i class="icon icon-folder"></i>
                  <span class="menu-title">Categories</span>
                </a>
            </li>
            <li class="menu-item menu-item-tags">
                <a href="/tags/">
                    <i class="icon icon-tags"></i>
                  <span class="menu-title">Tags</span>
                </a>
            </li>
            <li class="menu-item menu-item-about">
                <a href="/about/">
                    <i class="icon icon-cup-fill"></i>
                  <span class="menu-title">About</span>
                </a>
            </li>
        </ul>
      </nav>
    </div>
  </header>

<aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    
      <div class="widget">
    <h3 class="widget-title">Board</h3>
    <div class="widget-body">
        <div id="board">
            <div class="content">enjoy~
            </div>
        </div>
    </div>
</div>

      <div class="widget">
    <h3 class="widget-title"> Categories</h3>
    <div class="widget-body">
        <ul class="category-list">
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/java-framework/" class="category-list-link">java-framework</a><span class="category-list-count">38</span></li>
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/java-front-end/" class="category-list-link">java-front-end</a><span class="category-list-count">11</span></li>
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/java-se/" class="category-list-link">java-se</a><span class="category-list-count">21</span></li>
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/java-senior/" class="category-list-link">java-senior</a><span class="category-list-count">4</span></li>
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/linux/" class="category-list-link">linux</a><span class="category-list-count">13</span></li>
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/tools/" class="category-list-link">tools</a><span class="category-list-count">1</span></li>
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/%E6%8A%80%E6%9C%AF%E6%9D%82%E7%83%A9/" class="category-list-link">技术杂烩</a><span class="category-list-count">4</span></li>
            <li class="category-list-item"><a href="https://rulerLwx.gitee.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/" class="category-list-link">数据库</a><span class="category-list-count">15</span></li>
        </ul>
    </div>
</div>
      <div class="widget">
    <h3 class="widget-title"> Tags</h3>
    <div class="widget-body">
        <ul class="tag-list">
            
            
            <li class="tag-list-item"><a href="https://rulerLwx.gitee.io/tags/jvm/" class="tag-list-link">jvm</a><span
                    class="tag-list-count">1</span></li>
            
            
            <li class="tag-list-item"><a href="https://rulerLwx.gitee.io/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" class="tag-list-link">多线程</a><span
                    class="tag-list-count">2</span></li>
            
            
            <li class="tag-list-item"><a href="https://rulerLwx.gitee.io/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/" class="tag-list-link">网络编程</a><span
                    class="tag-list-count">3</span></li>
            
        </ul>

    </div>
</div>
      
<div class="widget">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget-body">
        <ul class="recent-post-list list-unstyled no-thumbnail">
            <li>
                <div class="item-inner">
                    <p class="item-title">
                        <a href="https://rulerLwx.gitee.io/2020/07/%E8%87%AA%E5%B7%B1%E5%8A%A8%E6%89%8B%E4%B8%80%E6%89%B9%E9%87%8F%E7%A7%BB%E5%8A%A8%E9%87%8D%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6/" class="title">自己动手（一）——批量移动、重命名文件</a>
                    </p>
                    <p class="item-date">
                        <time datetime="2020-07-27 17:50:02 &#43;0800 CST" itemprop="datePublished">2020-07-27</time>
                    </p>
                </div>
            </li>
            <li>
                <div class="item-inner">
                    <p class="item-title">
                        <a href="https://rulerLwx.gitee.io/2020/07/%E5%85%B3%E4%BA%8Einteger%E7%9A%84-128~127%E7%BC%93%E5%AD%98/" class="title">关于Integer的 -128~127缓存</a>
                    </p>
                    <p class="item-date">
                        <time datetime="2020-07-11 16:56:21 &#43;0800 CST" itemprop="datePublished">2020-07-11</time>
                    </p>
                </div>
            </li>
            <li>
                <div class="item-inner">
                    <p class="item-title">
                        <a href="https://rulerLwx.gitee.io/2020/07/%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98/" class="title">跨域问题</a>
                    </p>
                    <p class="item-date">
                        <time datetime="2020-07-08 22:41:12 &#43;0800 CST" itemprop="datePublished">2020-07-08</time>
                    </p>
                </div>
            </li>
            <li>
                <div class="item-inner">
                    <p class="item-title">
                        <a href="https://rulerLwx.gitee.io/2020/07/%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/" class="title">个人博客搭建</a>
                    </p>
                    <p class="item-date">
                        <time datetime="2020-07-05 18:58:47 &#43;0800 CST" itemprop="datePublished">2020-07-05</time>
                    </p>
                </div>
            </li>
            <li>
                <div class="item-inner">
                    <p class="item-title">
                        <a href="https://rulerLwx.gitee.io/2020/07/centos/" class="title">CentOS-</a>
                    </p>
                    <p class="item-date">
                        <time datetime="2020-07-01 18:58:47 &#43;0800 CST" itemprop="datePublished">2020-07-01</time>
                    </p>
                </div>
            </li>
        </ul>
    </div>
</div>
  </div>
</aside>

    
    
<aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    <h4 class="toc-title">Catalogue</h4>
    <nav id="toc" class="js-toc toc">

    </nav>
  </div>
</aside>
<main class="main" role="main"><div class="content">
  <article id="-" class="article article-type-" itemscope
    itemtype="http://schema.org/BlogPosting">
    
    <div class="article-header">
      <h1 itemprop="name">
  <a
    class="article-title"
    href="/2020/07/java-framework-springmvc-2/"
    >Java-framework-SpringMVC-2</a
  >
</h1>

      <div class="article-meta">
        
<span class="article-date">
  <i class="icon icon-calendar-check"></i>&nbsp;
<a href="https://rulerLwx.gitee.io/2020/07/java-framework-springmvc-2/" class="article-date">
  <time datetime="2020-07-01 18:58:47 &#43;0800 CST" itemprop="datePublished">2020-07-01</time>
</a>
</span>
<span class="article-category">
  <i class="icon icon-folder"></i>&nbsp;
  <a class="article-category-link" href="/categories/java-framework/"> Java-framework </a>
</span>

        <span class="post-comment"><i class="icon icon-comment"></i>&nbsp;<a href="/2020/07/java-framework-springmvc-2/#comments"
            class="article-comment-link">Comments</a></span>
		<span class="post-wordcount hidden-xs" itemprop="wordCount">Word Count: 8479words</span>
		<span class="post-readcount hidden-xs" itemprop="timeRequired">Read Count: 17minutes </span>
      </div>
    </div>
    <div class="article-entry marked-body js-toc-content" itemprop="articleBody">
      <h1 id="数据格式转换">数据格式转换</h1>
<h2 id="入参">入参</h2>
<p>数据格式化，如把日期格式化为 yyyy-MM-dd。</p>
<h3 id="mvcannotation-driven配置的作用"><a href="mvc:annotation-driven/">mvc:annotation-driven/</a>配置的作用</h3>
<p><a href="mvc:annotation-driven/">mvc:annotation-driven/</a>会自动注册如下三个bean：</p>
<ul>
<li>RequestMappingHandlerMapping</li>
<li>RequestMappingHandlerAdapter</li>
<li>ExceptionHandlerExceptionResolver</li>
</ul>
<p>除此外，还提供如下支持：</p>
<ul>
<li>支持使用ConversionService实例（默认是FormattingConversionServiceFactoryBean）对表单参数进行类型转换</li>
<li>支持使用 @NumberFormatannotation、@DateTimeFormat 注解完成数据类型的格式化</li>
<li>支持使用 @Valid 注解对JavaBean实例进行JSR303验证</li>
<li>支持使用 @RequestBody 和 @ResponseBody 注解</li>
</ul>
<h3 id="格式化">格式化</h3>
<p><a href="mvc:annotation-driven/">mvc:annotation-driven/</a>默认创建的ConversionService实例为FormattingConversionServiceFactoryBean，其内部已经注册了如下转换器：</p>
<ul>
<li>NumberFormatAnnotationFormatterFactory，支持对数字类型的属性使用 @NumberFormat 注解</li>
<li>JodaDateTimeFormatAnnotationFormatterFactory，支持对日期类型的属性使用 @DateTimeFormat 注解</li>
</ul>
<h4 id="datetimeformat">@DateTimeFormat</h4>
<p>可对java.util.Date、java.util.Calendar、java.long.Long 时间类型进行标注以进行格式化。</p>
<p>@DateTimeFormat 可设置如下属性：</p>
<ul>
<li>
<p>pattern 属性：类型为字符串。指定解析/格式化字段数据的模式，如：yyyy-MM-dd hh:mm:ss。</p>
</li>
<li>
<p>iso 属性：类型为 DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式，包括四种：</p>
<ul>
<li>ISO.NONE（不使用） &ndash; 默认</li>
<li>ISO.DATE(yyyy-MM-dd)</li>
<li>SO.TIME(hh:mm:ss.SSSZ)</li>
<li>ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)</li>
</ul>
</li>
<li>
<p>style 属性：字符串类型。通过样式指定日期时间的格式，由两位字符组成，第一位表示日期的格式，第二位表示时间的格式：S：短日期/时间格式、M：中日期/时间格式、L：长日期/时间格式、F：完整日期/时间格式、-：忽略日期或时间格式</p>
</li>
</ul>
<p>示例：</p>
<p>1）页面：2017-08-01</p>
<p>2）后台字段：</p>
<pre><code class="language-java">@DateTimeFormat(pattern=&quot;yyyy-MM-dd&quot;)
private Date birth;
</code></pre>
<p>也就是说，页面的格式跟pattern=&quot;yyyy-MM-dd&quot;一致，才能调试通过。</p>
<p>此注解使用的前提是在springmvc配置文件中配置<a href="mvc:annotation-driven/">mvc:annotation-driven/</a>注解。</p>
<h4 id="numberformat">@NumberFormat</h4>
<p>@NumberFormat拥有两个互斥的属性：</p>
<ul>
<li>style：类型为 NumberFormat.Style。用于指定样式类型，包括三种：
<ul>
<li>Style.NUMBER（正常数字类型）</li>
<li>Style.CURRENCY（货币类型）</li>
<li>Style.PERCENT（百分数类型）</li>
</ul>
</li>
<li>pattern：类型为 String，自定义样式， 如patter=&rdquo;#,###&quot;；</li>
</ul>
<p>示例</p>
<pre><code class="language-java">@NumberFormat(pattern=&quot;#,###,###.#&quot;)
private Float salary;
</code></pre>
<p>用#表示数字。</p>
<p>此注解使用的前提是在springmvc配置文件中配置<a href="mvc:annotation-driven/">mvc:annotation-driven/</a>注解。</p>
<h3 id="类型转换出错时如何捕捉呢">类型转换出错时，如何捕捉呢？</h3>
<pre><code class="language-java">@RequestMapping(value=&quot;/testFormatDate&quot;,method=RequestMethod.POST)
public String testFormatDate(PersonInfo person,BindingResult result){
	System.out.println(person.getBirth());
	if(result.getErrorCount()&gt;0	){
		System.out.println(&quot;出错了&quot;);
		for(FieldError err:result.getFieldErrors()){
			System.out.println(err.getField()+&quot;:&quot;+err.getDefaultMessage());
		}
	}
	return SUCESSED;
}
</code></pre>
<h3 id="自定义类型转换器">自定义类型转换器</h3>
<p>1）实现接口，自定义类型转换器</p>
<p>Spring 定义了 3 种类型的转换器接口，实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactroyBean 中</p>
<ul>
<li>Converter&lt;S,T&gt;：将 S 类型对象转为 T 类型对象</li>
<li>ConverterFactory：将相同系列多个“同质”Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象（例如将 String 转换为 Number 及 Number 子类（Integer、Long、Double 等）对象）可使用该转换器工厂类</li>
<li>GenericConverter：会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换</li>
</ul>
<pre><code class="language-java">public class MyConversion implements Converter&lt;String, Date&gt; {
	public String datePattern;
	
	public MyConversion(String datePattern) {
		this.datePattern = datePattern;
		System.out.println(&quot;dataPattern:&quot;+datePattern);
	}

	@Override
	public Date convert(String source) {
		try {
			System.out.println(&quot;source:&quot;+source);
			SimpleDateFormat sf = new SimpleDateFormat(datePattern);
			sf.setLenient(false);
			return sf.parse(source);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
}
</code></pre>
<p>2）将类型转换器注册到ConversionServiceFactroyBean中，也可以是FormattingConversionServiceFactoryBean</p>
<pre><code class="language-xml">	&lt;bean id=&quot;conversionService&quot;
		class=&quot;org.springframework.context.support.ConversionServiceFactoryBean&quot;&gt;
		&lt;property name=&quot;converters&quot;&gt;
			&lt;set&gt;
				&lt;bean class=&quot;com.demo.conversion.MyConversion&quot;&gt;
					&lt;constructor-arg type=&quot;java.lang.String&quot; value=&quot;yyyy-MM-dd&quot; /&gt;
				&lt;/bean&gt;
			&lt;/set&gt;
		&lt;/property&gt;
	&lt;/bean&gt;
</code></pre>
<p>3）注册类型转换器</p>
<pre><code class="language-xml">&lt;mvc:annotation-driven conversion-service=&quot;conversionService&quot;/&gt;
</code></pre>
<p>将自定义的 ConversionService 注册到Spring MVC 的上下文中。conversionService是ConversionServiceFactroyBean的id名称。</p>
<p>4）页面</p>
<pre><code class="language-html">&lt;a href=&quot;test/testConversion?apply_time=08/01/2017&quot;&gt;测试自定义转换器testConversion&lt;/a&gt;
</code></pre>
<p>5） Controller</p>
<pre><code class="language-java">@Controller
@RequestMapping(&quot;/test&quot;)
public class TestController {
    @RequestMapping(&quot;/testConversion&quot;)
    public String testConversion(@RequestParam(&quot;apply_time&quot;)Date apply_time){
        System.out.println(apply_time);
        return &quot;addUser&quot;;
    }
}
</code></pre>
<p>此时 apply_time=08/01/2017 是不能转换的</p>
<pre><code class="language-txt">source:08/01/2017
java.text.ParseException: Unparseable date: &quot;08/01/2017&quot;
	at java.text.DateFormat.parse(DateFormat.java:366)
</code></pre>
<h2 id="出参">出参</h2>
<p>需要导入 jackson 依赖包（参考 JSON 章节）</p>
<h3 id="jsonformat">@JsonFormat</h3>
<pre><code class="language-java">    @JsonFormat(pattern=&quot;yyyy-MM-dd&quot;,timezone=&quot;GMT+8&quot;)
    private Date birthday;
</code></pre>
<p><a href="https://blog.csdn.net/qq_28483283/article/details/81326365">https://blog.csdn.net/qq_28483283/article/details/81326365</a></p>
<h2 id="总结">总结</h2>
<p>在字段上添加注解，其实都是springmvc在做工作，如果在controller中打印从数据库中获取的数据，是不按&quot;yyyy-MM-dd&quot;格式出现的，在终端才会显示，所以是springmvc在做工作。</p>
<p>其原理是 HttpMessageConverter，请参考 JSON 章节。</p>
<p>user.java</p>
<pre><code class="language-java">    @DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)
    @JsonFormat(pattern=&quot;yyyy-MM-dd&quot;,timezone=&quot;GMT+8&quot;)
    private Date birthday;
</code></pre>
<p>controller</p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/getUser&quot;)
    public List&lt;User&gt; getUser(String name, Model model){
        List&lt;User&gt; userList = userService.getUser(name);

        for(User user: userList) {
            System.out.println(user.getName()+&quot;,&quot;+user.getBirthday());
        }

        return userList;
    }
</code></pre>
<p>控制台</p>
<pre><code class="language-txt">wangwu,Sun May 12 00:00:00 GMT+08:00 2019
wangwu,Sun May 12 00:00:00 GMT+08:00 2019
wangwu,Sun May 12 00:00:00 GMT+08:00 2019
</code></pre>
<p>浏览器</p>
<pre><code class="language-json">[{&quot;id&quot;:47,&quot;name&quot;:&quot;wangwu&quot;,&quot;birthday&quot;:&quot;2019-05-12&quot;,&quot;email&quot;:&quot;wangwu@163.com&quot;},{&quot;id&quot;:48,&quot;name&quot;:&quot;wangwu&quot;,&quot;birthday&quot;:&quot;2019-05-12&quot;,&quot;email&quot;:&quot;wangwu@163.com&quot;},{&quot;id&quot;:49,&quot;name&quot;:&quot;wangwu&quot;,&quot;birthday&quot;:&quot;2019-05-12&quot;,&quot;email&quot;:&quot;wangwu@163.com&quot;}]
</code></pre>
<h1 id="表单校验">表单校验</h1>
<h2 id="传统官方校验">传统（官方）校验</h2>
<p>在 Spring MVC 中，可直接通过注解驱动的方式进行数据校验。</p>
<p>Spring的 LocalValidatorFactroyBean 既实现了 Spring 的 Validator 接口，也实现了JSR 303 的 Validator 接口。</p>
<p>只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean，即可将其注入到需要数据校验的 Bean 中。</p>
<p>Spring 本身并没有提供 JSR303 的实现，所以必须将JSR303 的实现者的 jar 包放到类路径下。</p>
<p><a href="mvc:annotation-driven/">mvc:annotation-driven/</a> 会默认装配好一个 LocalValidatorFactoryBean，通过在处理方法的入参上标注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作。</p>
<p>Spring MVC 是通过对处理方法签名的规约来保存校验结果的：前一个表单/命令对象的校验结果保存到随后的入参中，这个保存校验结果的入参必须是 BindingResult 或 Errors 类型，这两个类都位于 org.springframework.validation 包中。</p>
<p>JSR303</p>
<ul>
<li>@Null    被注释的元素必须为 null</li>
<li>@NotNull    被注释的元素必须不为 null</li>
<li>@AssertTrue    被注释的元素必须为 true</li>
<li>@AssertFalse    被注释的元素必须为 false</li>
<li>@Min(value)    被注释的元素必须是一个数字，其值必须大于等于指定的最小值</li>
<li>@Max(value)    被注释的元素必须是一个数字，其值必须小于等于指定的最大值</li>
<li>@DecimalMin(value)    被注释的元素必须是一个数字，其值必须大于等于指定的最小值</li>
<li>@DecimalMax(value)    被注释的元素必须是一个数字，其值必须小于等于指定的最大值</li>
<li>@Size(max, min)    被注释的元素的大小必须在指定的范围内</li>
<li>@Digits (integer, fraction)    被注释的元素必须是一个数字，其值必须在可接受的范围内</li>
<li>@Past    被注释的元素必须是一个过去的日期</li>
<li>@Future    被注释的元素必须是一个将来的日期</li>
<li>@Pattern(value)    被注释的元素必须符合指定的正则表达式</li>
</ul>
<p>Hibernate Validator 附加的 constraint：</p>
<ul>
<li>@Email    被注释的元素必须是电子邮箱地址</li>
<li>@Length    被注释的字符串的大小必须在指定的范围内</li>
<li>@NotEmpty    被注释的字符串的必须非空</li>
<li>@Range    被注释的元素必须在合适的范围内</li>
</ul>
<p>校验步骤：</p>
<p>1）加入Hibernate Validator验证框架Jar包。</p>
<pre><code class="language-xml">&lt;dependency&gt;
	&lt;groupId&gt;org.hibernate&lt;/groupId&gt;
	&lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;
	&lt;version&gt;5.3.5.Final&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>2）在springmvc配置文件中添加 <a href="mvc:annotation-driven/">mvc:annotation-driven/</a>。</p>
<p>3）在bean的对应属性上添加对应的注解。</p>
<pre><code class="language-java">public class PersonInfo {
	@NotEmpty
	private String username;
	@Email
	private String email;
</code></pre>
<p>4）在目标方法Bean类型的前面加上 @Valid 注解。</p>
<pre><code class="language-java">@RequestMapping(value=&quot;/testValidator&quot;,method=RequestMethod.POST)
public String testValidator(@Valid PersonInfo person,BindingResult result){
	System.out.println(&quot;testValidator:&quot;);
	if(result.getErrorCount()&gt;0	){
		System.out.println(&quot;出错了&quot;);
		for(FieldError err:result.getFieldErrors()){
			System.out.println(err.getField()+&quot;:&quot;+err.getDefaultMessage());
		}
	}
	return SUCESSED;
}
</code></pre>
<p>注意：BindingResult 必须在被校验字段或对象的后面，中间不能有其它参数。</p>
<p>5）页面</p>
<pre><code class="language-html">&lt;form action=&quot;requestParamTest/testValidator&quot; method=&quot;post&quot;&gt;
	用户名：&lt;input type=&quot;text&quot; name=&quot;username&quot;&gt;&lt;br&gt;
	邮箱：&lt;input type=&quot;text&quot; name=&quot;email&quot;&gt;&lt;br&gt;
	&lt;input type=&quot;submit&quot; value=&quot;测试  testValidator 请求&quot;&gt;
&lt;/form&gt;
</code></pre>
<p>增加map入参，还可以回显数据（需要使用springmvc标签）TODO&hellip;thinking&hellip;..</p>
<p>参考：https://www.cnblogs.com/myinspire/articles/7649027.html</p>
<p>如何校验一个整数，如年龄？TODO&hellip;</p>
<h2 id="自定义校验">自定义校验</h2>
<p>需求：定义一个校验手机号码的注解</p>
<pre><code class="language-java">public class IsMobileValidator implements ConstraintValidator&lt;IsMobile ,String &gt;{

	private boolean  required = false;
	
	public void initialize(IsMobile constraintAnnotation) {
		required = constraintAnnotation.required();
	}

	public boolean isValid(String value, ConstraintValidatorContext context) {
		if(required) {
			return ValidatorUtil.isMobile(value);
		}else {
			if(StringUtils.isEmpty(value)) {
				return true;
			}else {
				return ValidatorUtil.isMobile(value);
			}
		}
	}

}
</code></pre>
<pre><code class="language-java">@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {
    String message() default &quot;手机号码格式有误&quot;;  
    Class&lt;?&gt;[] groups() default {};  
    Class&lt;? extends Payload&gt;[] payload() default {};  
    boolean required() default true;
}
</code></pre>
<p>总结：这个需求，能否用<code>@Pattern(regexp = &quot;1\\d{10}&quot;,message = &quot;手机号码格式有误&quot;)</code>来解决？</p>
<p>TODO&hellip;</p>
<h1 id="json">JSON</h1>
<h2 id="使用json">使用JSON</h2>
<h3 id="步骤一加入json的jar包">步骤一：加入JSON的Jar包</h3>
<p>注意：Spring默认使用  JackSon，所以加入jackson依赖包，分1.x和2.x版本</p>
<p>2.x版本：</p>
<pre><code class="language-xml">    &lt;properties&gt;
        ...
        &lt;jackson.version&gt;2.9.8&lt;/jackson.version&gt;
        ...
    &lt;/properties&gt;
    
    &lt;!--jackson--&gt;
    &lt;!--自动包含了 jackson-core/jackson-databind/jackson-annotation--&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
        &lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;
        &lt;version&gt;${jackson.version}&lt;/version&gt;
    &lt;/dependency&gt;
</code></pre>
<p>1.x版本（tecsun）：</p>
<pre><code class="language-xml">&lt;dependency&gt;
	&lt;groupId&gt;org.codehaus.jackson&lt;/groupId&gt;
	&lt;artifactId&gt;jackson-mapper-asl&lt;/artifactId&gt;
	&lt;version&gt;1.9.13&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
	&lt;groupId&gt;org.codehaus.jackson&lt;/groupId&gt;
	&lt;artifactId&gt;jackson-core-asl&lt;/artifactId&gt;
	&lt;version&gt;1.9.13&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>1.x 参考：https://github.com/FasterXML/jackson-1</p>
<p>结合笔记《Java-framework-JSON》进行学习比较。</p>
<h3 id="步骤二用responsebody修饰处理方法中并返回要生成的json数据">步骤二：用@ResponseBody修饰处理方法中，并返回要生成的JSON数据。</h3>
<pre><code class="language-java">@RequestMapping(value=&quot;/testJson&quot;)
@ResponseBody
public Object testJson(){
	ArrayList&lt;PersonInfo&gt; list = new ArrayList&lt;PersonInfo&gt;();
	list.add(new PersonInfo(&quot;张三1&quot;, &quot;zs@163.com&quot;));
	list.add(new PersonInfo(&quot;张三2&quot;, &quot;zs@163.com&quot;));
	list.add(new PersonInfo(&quot;张三3&quot;, &quot;zs@163.com&quot;));
	return list;
}
</code></pre>
<h2 id="底层原理">底层原理</h2>
<p>之所以可以生成JSON，底层使用的是HttpMessageConverter<!-- raw HTML omitted -->，其 是 Spring3.0 新添加的一个接口，负责将请求信息转换为一个对象（类型为 T），将对象（类型为 T）输出为响应信息。</p>
<p>HttpMessageConverter<!-- raw HTML omitted -->接口定义的方法：</p>
<ul>
<li>Boolean canRead(Class&lt;?&gt; clazz,MediaType mediaType): 指定转换器可以读取的对象类型，即转换器是否可将请求信息转换为 clazz 类型的对象，同时指定支持 MIME 类型(text/html,applaiction/json等)。</li>
<li>Boolean canWrite(Class&lt;?&gt; clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中，响应流支持的媒体类型在MediaType 中定义。</li>
<li>LIst<!-- raw HTML omitted --> getSupportMediaTypes()：该转换器支持的媒体类型。</li>
<li>T read(Class&lt;? extends T&gt; clazz,HttpInputMessage inputMessage)将请求信息流转换为 T 类型的对象。</li>
<li>void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中，同时指定相应的媒体类型为 contentType。</li>
</ul>
<p>入参、出参的转换图示：</p>
<p><img src="https://gitee.com/rulerLwx/PicGo/raw/master/img-07/20200708120508.png" alt="image"></p>
<p>使用 HttpMessageConverter<!-- raw HTML omitted --> 将请求信息转化并绑定到处理方法的入参中或将响应结果转为对应类型的响应信息，Spring 提供了两种途径：</p>
<ul>
<li>使用 @RequestBody / @ResponseBody – 对处理方法进行标注</li>
<li>使用 HttpEntity<!-- raw HTML omitted --> / ResponseEntity<!-- raw HTML omitted --> 作为处理方法的入参或返回值
当控制器处理方法使用到@RequestBody/@ResponseBody或
HttpEntity<!-- raw HTML omitted --> /ResponseEntity<!-- raw HTML omitted --> 时, Spring 首先根据请求头或响应头的Accept 属性选择匹配的 HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的HttpMessageConverter 将报错。</li>
</ul>
<p>如何找到对应的 HttpMessageConverter ？—— 根据入参、返回值类型</p>
<p><img src="https://gitee.com/rulerLwx/PicGo/raw/master/img-07/20200708120525.png" alt="image"></p>
<p>@RequestBody和@ResponseBody不需要成对出现。</p>
<h2 id="requestbodyresponsebody使用示例">@RequestBody、@ResponseBody使用示例</h2>
<p>1）页面：</p>
<pre><code class="language-html">&lt;form action=&quot;requestParamTest/testHttpMessageConverter&quot; method=&quot;post&quot; 
	enctype=&quot;multipart/form-data&quot;&gt;
	文件：&lt;input type=&quot;file&quot; name=&quot;file&quot;&gt;
	描述：&lt;input type=&quot;text&quot; name=&quot;desc&quot;&gt;&lt;br&gt;
	&lt;input type=&quot;submit&quot; value=&quot;测试  testJson 请求&quot;&gt;
&lt;/form&gt;
</code></pre>
<p>2）后台：</p>
<pre><code class="language-java">@ResponseBody
@RequestMapping(value=&quot;/testHttpMessageConverter&quot;)
public String testHttpMessageConverter(@RequestBody String txt){
	System.out.println(txt);
	return &quot;hello world! &quot;+new Date();
}
</code></pre>
<p>使用了@ResponseBody后，就算返回的是String，也不跳转到页面了，而是直接向页面输出。</p>
<h2 id="httpentitytresponseentityt使用示例">HttpEntity<!-- raw HTML omitted -->、ResponseEntity<!-- raw HTML omitted -->使用示例</h2>
<p>ResponseEntity使用示例（相当于下载一个文件）：</p>
<p>1）页面</p>
<p><code>&lt;a href=&quot;requestParamTest/testResponseEntity&quot;&gt;testResponseEntity&lt;/a&gt;</code></p>
<p>2）后台</p>
<pre><code class="language-java">@RequestMapping(value=&quot;/testResponseEntity&quot;)
public ResponseEntity&lt;byte[]&gt; testResponseEntity(HttpSession session){
	byte[] body = null;
	try {
		ServletContext context = session.getServletContext();
		InputStream is = context.getResourceAsStream(&quot;/files/pom.xml&quot;);
		body = new byte[is.available()];
		is.read(body);
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	HttpHeaders headers = new HttpHeaders();
	headers.add(&quot;Content-Disposition&quot;, &quot;attachment;filename=pom.xml&quot;);
	
	HttpStatus statusCode = HttpStatus.OK;
	
	ResponseEntity&lt;byte[]&gt; responseEntity = 
			new ResponseEntity&lt;byte[]&gt;(body, headers, statusCode);
	return responseEntity;
}
</code></pre>
<h1 id="文件上传">文件上传</h1>
<p>Spring MVC 为文件上传提供了直接的支持，这种支持是通过即插即用的 MultipartResolver实现的。Spring用Jakarta Commons FileUpload技术实现了一个MultipartResolver 实现类：CommonsMultipartResolver。</p>
<p>Spring MVC 上下文中默认没有装配 MultipartResovler，因此默认情况下不能处理文件的上传工作，如果想使用 Spring 的文件上传功能，需现在上下文中配置 MultipartResolver。</p>
<p>1）加入Jar包</p>
<pre><code class="language-xml">&lt;dependency&gt;
	&lt;groupId&gt;commons-fileupload&lt;/groupId&gt;
	&lt;artifactId&gt;commons-fileupload&lt;/artifactId&gt;
	&lt;version&gt;1.2.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
	&lt;groupId&gt;commons-io&lt;/groupId&gt;
	&lt;artifactId&gt;commons-io&lt;/artifactId&gt;
	&lt;version&gt;2.0.1&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>1.4版本自动包含了common-io</p>
<pre><code class="language-xml">&lt;dependency&gt;
  &lt;groupId&gt;commons-fileupload&lt;/groupId&gt;
  &lt;artifactId&gt;commons-fileupload&lt;/artifactId&gt;
  &lt;version&gt;1.4&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>问：版本对jdk有要求吗？</p>
<p>答：Apache Commons FileUpload 1.4 (requires Java 1.6 or later)</p>
<p>参考：https://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi</p>
<p>2）在SpringMVC配置文件中配置MultipartResolver</p>
<pre><code class="language-xml">&lt;bean id=&quot;multipartResolver&quot; class=&quot;org.springframework.web.multipart.commons.CommonsMultipartResolver&quot;&gt;
	&lt;property name=&quot;defaultEncoding&quot; value=&quot;UTF-8&quot;/&gt;
	&lt;property name=&quot;maxUploadSize&quot; value=&quot;1024000&quot;/&gt;
&lt;/bean&gt;
</code></pre>
<p>注意：此处的property是根据setXxxx方法来确定的，不是字段了。</p>
<p>3）页面</p>
<pre><code class="language-html">&lt;form action=&quot;requestParamTest/testFileUpload&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
	文件：&lt;input type=&quot;file&quot; name=&quot;file&quot;&gt;
	描述：&lt;input type=&quot;text&quot; name=&quot;desc&quot;&gt;&lt;br&gt;
	&lt;input type=&quot;submit&quot; value=&quot;测试  testFileUpload 请求&quot;&gt;
&lt;/form&gt;
</code></pre>
<p>4）后台处理方法</p>
<pre><code class="language-java">@RequestMapping(value=&quot;/testFileUpload&quot;)
public String testFileUpload(@RequestParam(&quot;desc&quot;)String desc,@RequestParam(&quot;file&quot;)MultipartFile file) throws IOException{
	System.out.println(&quot;desc:&quot;+desc);
	System.out.println(&quot;file:&quot;+file.getOriginalFilename());
	System.out.println(&quot;InputStream:&quot;+file.getInputStream());
	return &quot;sucess.jsp&quot;;
}
</code></pre>
<p>由此看到，@RequestParam可以作用于post方式的参数。</p>
<h1 id="拦截器">拦截器</h1>
<h2 id="自定义拦截器">自定义拦截器</h2>
<p>Spring MVC也可以使用拦截器对请求进行拦截处理，用户可以自定义拦截器来实现特定的功能，自定义的拦截器必须实现HandlerInterceptor接口。该接口有如下方法：</p>
<ul>
<li>preHandle()：这个方法在业务处理器处理请求之前被调用，在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器，或者是业务处理器去进行处理，则返回true；如果程序员决定不需要再调用其他的组件去处理请求，则返回false。</li>
<li>postHandle()：这个方法在业务处理器处理完请求后，但是DispatcherServlet 向客户端返回响应前被调用，在该方法中对用户请求request进行处理。</li>
<li>afterCompletion()：这个方法在 DispatcherServlet 完全处理完请求后被调用，可以在该方法中进行一些资源清理的操作。</li>
</ul>
<p>步骤：</p>
<p>1、定义一个类，实现HandlerInterceptor接口。</p>
<p>2、在springmvc的配置文件中增加拦截器配置：</p>
<pre><code class="language-xml">&lt;mvc:interceptors&gt;
	&lt;bean class=&quot;com.demo.interceptor.MyInterceptor&quot;/&gt;
&lt;/mvc:interceptors&gt;
</code></pre>
<p>可以配置多个&lt;bean&hellip;/&gt;。</p>
<p>日志可以在preHandle()方法里面写。</p>
<h2 id="拦截器配置">拦截器配置</h2>
<p>配置拦截器时，可以配置（不）拦截的路径，如下：</p>
<pre><code class="language-xml">&lt;mvc:interceptors&gt;
	&lt;bean class=&quot;com.demo.interceptor.MyInterceptor&quot;/&gt;
	&lt;mvc:interceptor&gt;
		&lt;mvc:mapping path=&quot;/user&quot;/&gt;
		&lt;bean class=&quot;com.demo.interceptor.MyInterceptor2&quot;/&gt;
	&lt;/mvc:interceptor&gt;
&lt;/mvc:interceptors&gt;
</code></pre>
<h2 id="拦截器顺序">拦截器顺序</h2>
<p>遵循先进后出原则。顺序跟&lt;mvc:interceptors &hellip;/&gt;中定义的顺序一致，即定义在前面的排序在前面。</p>
<p>正常执行的情况：</p>
<p><img src="https://gitee.com/rulerLwx/PicGo/raw/master/img-07/20200708120551.png" alt="image"></p>
<p>异常的情况：</p>
<p>如果SecondInterceptor#preHandle时返回flase，则不执行下面的过程，直接执行到FirstInterceptor#afterCompletion。</p>
<h2 id="拦截器原理">拦截器原理</h2>
<p>springmvc拦截器使用和原理理解：https://blog.csdn.net/jiangyie000/article/details/80691186</p>
<h1 id="异常处理">异常处理</h1>
<h2 id="概述">概述</h2>
<p>Spring MVC 通过 HandlerExceptionResolver 处理程序的异常，包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。</p>
<p>如果使用了<a href="mvc:annotation-driven.../">mvc:annotation-driven.../</a>配置，则DispatcherServlet 默认装配的异常处理如下：</p>
<ul>
<li>
<p>ExceptionHandlerExceptionResolver</p>
<ul>
<li>Controller类中使用 @ExceptionHandler 定义的方法将被用来处理当前controller的异常（<strong>controller级别</strong>），可以声明多个。</li>
<li>@ExceptionHandler 注解定义的方法优先级问题：例如发生的是NullPointerException，但是声明的异常有RuntimeException 和 Exception，此时会根据异常的最近继承关系找到继承深度最浅的那个@ExceptionHandler注解方法。</li>
<li>@ControllerAdvice 用于注解类（<strong>全局异常</strong>），如果在当前Controller类中找不到@ExceptionHandler注解的方法的话，会在Spring容器中查找使用 @ControllerAdvice 注解的类中的用 @ExceptionHandler 注解的方法来处理异常。</li>
</ul>
</li>
<li>
<p>ResponseStatusExceptionResolver，使用@ResponseStatus自定义异常类或方法。</p>
<p>@ResponseStatus可指定以下两个值：</p>
<ul>
<li>value，指定一个HttpStatus的常量值。</li>
<li>reason，异常文本消息。</li>
</ul>
</li>
<li>
<p>DefaultHandlerExceptionResolver，SpringMVC默认实现并提供的异常处理类。</p>
</li>
</ul>
<h2 id="exceptionhandlerexceptionresolver">ExceptionHandlerExceptionResolver</h2>
<p>ExceptionHandlerExceptionResolver，常用@ExceptionHandler和@ControllerAdvice来处理异常。</p>
<h3 id="exceptionhandler">@ExceptionHandler</h3>
<p>在Controller方法上使用@ExceptionHandler注解处理异常，其value属性接收一个异常类组成的数组。</p>
<p>注意：@ExceptionHandler 处理的是当前 <strong>controller 级别</strong>的异常，不是全局的异常。</p>
<p>示例：</p>
<pre><code class="language-java">@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleException(Exception ex){
	System.out.println(&quot;出错了：&quot;+ex);
	ModelAndView mv = new ModelAndView(&quot;error&quot;);
	mv.addObject(&quot;exception&quot;, ex);
	return mv;
}
</code></pre>
<p>注意：</p>
<ol>
<li>入参增加 Exception ex ，即是发生异常时所封装的异常对象；</li>
<li>如果要将错误信息传到页面，则返回值必须是ModelAndView ，如 new ModelAndView(&ldquo;error&rdquo;) 就可以传到“error.jsp”。</li>
<li>也可以返回JSON数据，这里只是举例</li>
</ol>
<pre><code class="language-java">    @ResponseBody
    @ExceptionHandler({Exception.class})
    public List&lt;User&gt; handleException(Exception ex){
        System.out.println(&quot;出错了：&quot;+ex);
        return userService.listUsers();
    }
</code></pre>
<h3 id="controlleradvice--exceptionhandler">@ControllerAdvice + @ExceptionHandler</h3>
<p>定义一个类，用@ControllerAdvice修饰，其中的方法用@ExceptionHandler修饰，这个类就是<strong>全局</strong>的异常处理了（记得把此类交给Spring管理）。</p>
<pre><code class="language-java">@ControllerAdvice
public class MyControllerExceptionHandler {

    @ResponseBody
    @ExceptionHandler({Exception.class})
    public String handleException(Exception ex) {
        System.out.println(&quot;出错了：&quot;+ex);
        String message = ex.getMessage();
        return &quot;error&quot;;
    }
}
</code></pre>
<p>注意：Spring MVC 配置文件需要配置 <a href="mvc:annotation-driven/">mvc:annotation-driven/</a>，否则此异常类不起作用。</p>
<p>参考：https://blog.csdn.net/x6582026/article/details/76400422</p>
<p>入参可以有哪些？</p>
<ul>
<li>HttpServletRequest request</li>
<li>Throwable ex</li>
<li>Exception ex ——20190817</li>
</ul>
<p>（@ControllerAdvice标记的类，就相当于一个controller，入参、返回值相同）</p>
<h2 id="responsestatusexceptionresolver">ResponseStatusExceptionResolver</h2>
<p>ResponseStatusExceptionResolver的使用是通过@ResponseStatus注解来实现的，@ResponseStatus可以修饰自定义的异常类，也可以修饰Controller里面的方法。</p>
<p>1） 作用在自定义异常类上</p>
<p>在自定义的类上加上@ResponseStatus修饰，指定value、reason属性。然后在Controller处理方法中抛出自定义的异常。</p>
<p>2） 作用在controller方法上</p>
<p>TODO&hellip;</p>
<h2 id="defaulthandlerexceptionresolver">DefaultHandlerExceptionResolver</h2>
<p>SpringMVC默认就定义了很多异常处理器。在DefaultHandlerExceptionResolver#doResolveException()方法中可以看到。</p>
<h2 id="simplemappingexceptionresolver">SimpleMappingExceptionResolver</h2>
<p>当出现异常时，可以通过SimpleMappingExceptionResolver来将异常映射跳转到哪个页面上。</p>
<p>示例：</p>
<p>1）在springmvc配置文件上，配置异常映射</p>
<pre><code class="language-xml">&lt;bean class=&quot;org.springframework.web.servlet.handler.SimpleMappingExceptionResolver&quot;&gt;
	&lt;property name=&quot;exceptionMappings&quot;&gt;
		&lt;props&gt;
			&lt;prop key=&quot;java.lang.ArrayIndexOutOfBoundsException&quot;&gt;error&lt;/prop&gt;
		&lt;/props&gt;
	&lt;/property&gt;
&lt;/bean&gt;
</code></pre>
<p>2）后台方法，当数组索引越界时，将出现异常</p>
<pre><code class="language-java">@RequestMapping(value=&quot;/testSimpleMappingExceptionResolver&quot;)
public String testSimpleMappingExceptionResolver(int i){
	String[] arr = {&quot;a&quot;,&quot;b&quot;,&quot;c&quot;,&quot;d&quot;};
	System.out.println(&quot;arr[&quot;+i+&quot;]:&quot;+arr[i] );
	return SUCESSED;
}
</code></pre>
<p>3）调用方法的页面</p>
<pre><code class="language-html">&lt;a href=&quot;requestParamTest/testSimpleMappingExceptionResolver?i=1&quot;&gt;
测试 testSimpleMappingExceptionResolver 
&lt;/a&gt;
</code></pre>
<p>4）error页面</p>
<p>${requestScope.exception }</p>
<h2 id="总结-1">总结</h2>
<p>ExceptionHandlerExceptionResolver 提供的 @ExceptionHandler 和 @ControllerAdvice 分别对应 controller级别、全局级别的异常处理方式。</p>
<p>那么如何处理service/dao/converter等等出现的异常呢？(下面是我的总结-2019)</p>
<ol>
<li>controller/service/dao中的异常都可以往外抛，然后在controller中用 @ExceptionHandler 定义方法来处理相应的异常</li>
<li>converter中的异常，TODO&hellip;</li>
</ol>
<p>如下面的by zero异常可以处理：</p>
<pre><code class="language-java">@Controller
@RequestMapping(&quot;/test&quot;)
public class TestController {
    @RequestMapping(&quot;/testConversion&quot;)
    public String testConversion(@RequestParam(&quot;apply_time&quot;)Date apply_time){
        System.out.println(apply_time);

        int i =10/0;    //这里异常

        return &quot;addUser&quot;;
    }

    @ExceptionHandler({Exception.class})
    public ModelAndView handleException(Exception ex){
        System.out.println(&quot;出错了：&quot;+ex);
        ModelAndView mv = new ModelAndView(&quot;error&quot;);
        mv.addObject(&quot;exception&quot;, ex);
        return mv;
    }
}
</code></pre>
<p>但在 @RequestParam(&ldquo;apply_time&rdquo;)Date apply_time 对应的处理器转换时出现异常，controller中的用@ExceptionHandler标记的方法却处理不了</p>
<hr>
<p>总结——异常处理方式：</p>
<p>1）在 controller 层调用service 层方法，service 层使用 throw 抛出异常，然后使用全局异常类捕捉异常</p>
<p>注：全局异常是指<code>ControllerAdvice + @ExceptionHandler</code>定义的类，其中使用<code>instanceof</code>判断异常种类</p>
<pre><code class="language-java">@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
	
	@ExceptionHandler(value=Exception.class)
	public  Result&lt;String&gt; exceptionHandler(HttpServletRequest request,Exception e){
		e.printStackTrace();
		if(e instanceof GlobalException) {
			GlobalException ex = (GlobalException) e;
			return Result.error(ex.getCm());
		}else if(e instanceof BindException) {
			BindException ex = (BindException) e;
			List&lt;ObjectError&gt; errors = ex.getAllErrors();
			ObjectError error = errors.get(0);
			String msg = error.getDefaultMessage();
			
			return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));
		}else {
			return Result.error(CodeMsg.SERVER_ERROR);
		}
	}
}
</code></pre>
<p>这样写越来，controller、service 层非常简洁。</p>
<p>参考：https://github.com/keeperLee/miaosha_1/blob/master/src/main/java/com/imooc/miaosha/exception/GlobalExceptionHandler.java</p>
<p>2）</p>

    </div>
    <div class="article-footer">
<blockquote class="mt-2x">
  <ul class="post-copyright list-unstyled">
    <li class="post-copyright-link hidden-xs">
      <strong>Permalink: </strong>
      <a href="https://rulerLwx.gitee.io/2020/07/java-framework-springmvc-2/" title="Java-framework-SpringMVC-2" target="_blank" rel="external">https://rulerLwx.gitee.io/2020/07/java-framework-springmvc-2/</a>
    </li>
    <li class="post-copyright-license">
      <strong>License：</strong><a href="http://creativecommons.org/licenses/by/4.0/deed.zh" target="_blank" rel="external">CC BY 4.0 CN</a>
    </li>
  </ul>
</blockquote>

<div class="panel panel-default panel-badger">
  <div class="panel-body">
    <figure class="media">
      <div class="media-left">
        <a href="https://gitee.com/rulerLwx" target="_blank" class="img-burn thumb-sm visible-lg">
          <img src="https://rulerLwx.gitee.io/avatar.png" class="img-rounded w-full" alt="">
        </a>
      </div>
      <div class="media-body">
        <h3 class="media-heading"><a href="https://gitee.com/rulerLwx" target="_blank"><span class="text-dark">rulerLwx</span><small class="ml-1x">thinking...</small></a></h3>
        <div>Good Good Study, Day Day Up~</div>
      </div>
    </figure>
  </div>
</div>
    </div>
  </article>
<section id="comments">
    <div id="vcomments"></div>
</section>

</div><nav class="bar bar-footer clearfix" data-stick-bottom>
    <div class="bar-inner">
        <ul class="pager pull-left">
            <li class="prev">
                <a href="https://rulerLwx.gitee.io/2020/07/java-framework-springmvc-3-%E6%95%B4%E5%90%88%E5%BC%80%E5%8F%91/" title="Java-framework-SpringMVC-3-整合开发"><i
                        class="icon icon-angle-left"
                        aria-hidden="true"></i><span>&nbsp;&nbsp;Older</span></a>
            </li>
            <li class="next">
                <a href="https://rulerLwx.gitee.io/2020/07/java-framework-springmvc-1/"
                    title="Java-framework-SpringMVC-1"><span>Newer&nbsp;&nbsp;</span><i
                        class="icon icon-angle-right" aria-hidden="true"></i></a>
            </li>
            
            <li class="toggle-toc">
                <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
                    title="Catalogue" role="button">
                    <span>[&nbsp;</span><span>Catalogue</span>
                    <i class="text-collapsed icon icon-anchor"></i>
                    <i class="text-in icon icon-close"></i>
                    <span>]</span>
                </a>
            </li>
        </ul>
        <div class="bar-right">
            <div class="share-component" data-sites="weibo,qq,wechat"
                data-mobile-sites="weibo,qq,qzone"></div>
        </div>
    </div>
</nav>

</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
<ul class="social-links">
    <li><a href="https://gitee.com/rulerLwx" target="_blank" title="gitee" data-toggle=tooltip data-placement=top >
            <i class="icon icon-gitee"></i></a></li>
    <li><a href="https://github.com/wolf-lea" target="_blank" title="github" data-toggle=tooltip data-placement=top >
            <i class="icon icon-github"></i></a></li>
</ul>
  <div class="copyright">
    &copy;2020  -
    2020
    <div class="publishby">
        Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
    </div>
  </div>
</footer>

<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_SVG"></script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
            showMathMenu: false, //disables context menu
            tex2jax: {
            inlineMath: [ ['$','$'], ['\\(','\\)'] ]
           }
    });
</script>


<script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
<script>
    window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
</script>
<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/python.min.js" defer></script>
<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/javascript.min.js" defer></script><script>
    hljs.configure({
        tabReplace: '    ', 
        classPrefix: ''     
        
    })
    hljs.initHighlightingOnLoad();
</script>
<script src="https://rulerLwx.gitee.io/js/application.min.bdeb64b910570b6c41badc6a05b7afb0c8ad9efd8525de3c7257d59e786326a3.js"></script>
<script src="https://rulerLwx.gitee.io/js/plugin.min.51ff8c7317566f82259170fa36e09c4493adc9b9378b427a01ad3f017ebac7dd.js"></script>

<script>
    (function (window) {
        var INSIGHT_CONFIG = {
            TRANSLATION: {
                POSTS: 'Posts',
                PAGES: 'Pages',
                CATEGORIES: 'Categories',
                TAGS: 'Tags',
                UNTITLED: '(Untitled)',
            },
            ROOT_URL: 'https:\/\/rulerLwx.gitee.io',
            CONTENT_URL: 'https:\/\/rulerLwx.gitee.io\/searchindex.json ',
        };
        window.INSIGHT_CONFIG = INSIGHT_CONFIG;
    })(window);
</script>
<script type="text/javascript" src="https://rulerLwx.gitee.io/js/insight.min.a343cd9a5a7698336b28ef3a7c16a3a1b1d2d5fb17dc8ed04022bbe08cc5459073a15bdafa3a8a58cdd56080784bdd69fa70b1ae8597565c799c57ed00f0e120.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.min.js"></script>
<script>
    tocbot.init({
        
        tocSelector: '.js-toc',
        
        contentSelector: '.js-toc-content',
        
        headingSelector: 'h1, h2, h3',
        
        hasInnerContainers: true,
    });
</script>

<script src="https://cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine"></script>
<script type="text/javascript">
    var GUEST = ['nick', 'mail', 'link'];
    var meta = 'nick,mail';
    meta = meta.split(',').filter(function (item) {
        return GUEST.indexOf(item) > -1;
    });
    new Valine({
        el: '#vcomments',
        verify: null ,
        notify: null ,
        appId: 'IyAB0PSPRazTPDxitO1ddQ7O-gzGzoHsz',
        appKey: '5rBJTq4KidYF33eXwvRVhtEH',
        placeholder: 'enjoy~',
        avatar: 'mm',
        meta: meta,
        pageSize: '10' || 10,
        visitor: false 
});
</script>

  </body>
</html>
